#include <gtest/gtest.h>

//      DB as interface for user  
//      1. db.stream();  [detail::DB]
//      2. db.shortcut();[detail::DB]
//      3. db.raw();      [detail::DB]
//      4. db is AutoOpenClose ( use AutoDB)
//
//      
//TODO: 
//  Exception vs system_error ??
//
//TODO: think more reset & rebind operation for in shortcut and stream ns
//  Log-support:  thread-safe 
// > SqliteDB  Debug inspect
//  Doxygen
//
//
//
//TODO:  Most important
//UT rethink the stmt 's lifetime : shared between the stmt & result ???? TEST(Two-case_for_smtm)
//TODO: remove the AbstractDB::raw_handler ??? all raw_handler ????
//UT Support basement:: QueryResult::Cursor explicit
//
//
// > BenchMark
//OK  Insert, replace, query[range, find-range]
//  , order-asc&desc, filter
//
//OK FileDB check the (dir/name) != :memory:... && not start with ":", details see sqlite.org::sqlite3_open
//OK  remove struct sqlite3_stmt declare  in all include file 
//OK  support: IDB -> DB(default)->[FileDB,MemoryDB], AutoDBDecorator  && AutoDB
//OK: refactor UnitTest into TestFixture --> first
//OK: product live code:  
//OK    - use forward declare
//OK    - divide into pieces
//OK    - namespace 
//OK    - Error Code -> use std::error_code;
//OK      - impl sqlite Error
//OK      - integrade std::error code
//OK: rename the Result -> ExecutionResult[ddl, dml];  QueryResult[dql]
//OK: add sqlite::shortcut for Easy use
//OK: create Statement with error_code
//OK: remove the duplicated code in DBTest && SqliteShortcutTest
//OK: create an AutoDB class for user
//OK:  OO-transaction support
//OK  write more unittest for helper::sqlite's reset && rebinding code;
//OK sqlite::DB is the same function for sqlite, IDB -> AbstractDB -> [sqlite::DB, sqlite::MemoryDB, sqlite::FileDB]

#include <filesystem>
#include <sqlite3.h>

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
  int i;
  for(i=0; i<argc; i++){
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
  }
  printf("\n");
  return 0;
}

TEST(SqliteTest, testMain){
  const char * dbname = "test.sqlite.db";
  sqlite3 *db;
  int rc = sqlite3_open(dbname, &db);
  ASSERT_TRUE(rc == 0);

  char *zErrMsg = 0;

  std::string sql  = "create table tab1 (one text, two int); ";
              sql += "insert into tab1 values('hello1', 10); ";
  rc = sqlite3_exec(db, sql.c_str(), callback, 0, &zErrMsg);
  ASSERT_TRUE(rc == SQLITE_OK);

  sqlite3_close(db);
  std::filesystem::remove(dbname);
}


//  support basement:: QueryResult::Cursor explicit
//TEST(QueryResultCursorTest, use){
//  QueryResult qs = query(); // query ok or not
//
//  if(qs && q.isOk()){
//    auto && cursor = qs.cursor(); 
//    int cc = cursor.columnCount();
//    while(cur){
//      auto && v = cursor.values(0..cc);
//      if(!v.isNull()){
//        v.string();
//      }
//      else{
//        "null";
//      }
//
//      cursor.next();
//    }
//  }
//}



/*  
//  rethink the stmt 's lifetime : shared between the stmt & result ???? TEST(Two-case_for_smtm)
TEST(Two-case_for_smtm-mem_management){ 
  {
    Result r;
    {
      auto && stmt  = db.createStatement();
      r = std::move(stmt.action());
    }
    r.foreach();
  }

  {
    auto && stmt = db.createStatement();

    Result && r = std::move(stmt.action());
    r.foreach();

    stmt.reset();
    Result && r =stmt.action();
    r.foreach();
  }

}*/

#include "helper/sqlite/detail/DB.h"
#include "helper/sqlite/detail/Statement.h"

TEST(SqliteDetailTest, use){
  helper::sqlite::detail::DB db("hello.db");
  ASSERT_EQ(0, db.open());

  {
    auto && p = db.prepare("BEGIN;");
    ASSERT_EQ(0, p.first);
    ASSERT_EQ(101, p.second->step());
  }

  {
    auto && p = db.prepare("END;");
    ASSERT_EQ(0, p.first);
    ASSERT_EQ(101, p.second->step());
  }
  

  ASSERT_EQ(0, db.close());

  //detail::Statement stmt;
}



